package org.dromara.system.controller.system;

import cn.dev33.satoken.annotation.SaIgnore;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.system.domain.SysSolutionDoc;
import org.dromara.system.mapper.SysSolutionDocMapper;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * 用户端 解决方案静态资源访问（匿名）
 * 特点：
 * 1) /user 前缀，便于前端直连打开与相对链接解析
 * 2) /file/{docId} 自动定位主 PDF
 * 3) /file/{docId}/{*filePath} 支持同方案目录内的受控资源（pdf/htm/html/png/jpg/svg/css/js 等）
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/user/pdf-solution")
@SaIgnore
public class SysUserPdfSolutionController {

    private final SysSolutionDocMapper solutionDocMapper;

    /**
     * 自动定位并预览主 PDF
     */
    @GetMapping("/file/{docId}")
    public void getUserMainPdf(@PathVariable Long docId, HttpServletResponse response) {
        SysSolutionDoc doc = solutionDocMapper.selectById(docId);
        if (doc == null) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }
        String main = doc.getMainFileName();
        if (!isSafeFileName(main) || !hasExt(main, "pdf")) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }
        String key = join(doc.getFolderPath(), main);
        streamStatic(key, main, response);
    }

    /**
     * 预览同方案目录内的受控静态资源（支持多级子路径）
     * Spring Boot 3 下使用 {*filePath} 捕获剩余路径
     */
    @GetMapping("/file/{docId}/{*filePath}")
    public void getUserStatic(@PathVariable Long docId,
                              @PathVariable String filePath,
                              HttpServletResponse response) {
        SysSolutionDoc doc = solutionDocMapper.selectById(docId);
        if (doc == null) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }
        String safe = normalize(filePath);
        if (!isSafePath(safe)) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            return;
        }
        // 白名单：按需增减
        if (!hasAnyExt(safe, "pdf", "htm", "html", "png", "jpg", "jpeg", "gif", "svg", "css", "js")) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            return;
        }
        String key = join(doc.getFolderPath(), safe);
        streamStatic(key, safe, response);
    }

    /* ----------------- 工具方法 ----------------- */

    private static String normalize(String raw) {
        if (raw == null) return null;
        String s;
        try {
            s = URLDecoder.decode(raw, StandardCharsets.UTF_8);
        } catch (Exception e) {
            s = raw;
        }
        s = s.trim();
        if (s.startsWith("./")) s = s.substring(2);
        while (s.startsWith("/") || s.startsWith("\\")) s = s.substring(1);
        return s;
    }

    private static boolean isSafePath(String s) {
        return s != null && !s.contains("..");
    }

    private static boolean isSafeFileName(String s) {
        return isSafePath(s) && s != null && !s.contains("/") && !s.contains("\\");
    }

    private static boolean hasExt(String name, String ext) {
        return name != null && name.toLowerCase().endsWith("." + ext);
    }

    private static boolean hasAnyExt(String name, String... exts) {
        if (name == null) return false;
        String n = name.toLowerCase();
        for (String e : exts) {
            if (n.endsWith("." + e)) return true;
        }
        return false;
    }

    private static String join(String dir, String file) {
        String base = dir == null ? "" : dir;
        if (!base.endsWith("/")) base += "/";
        return base + file;
    }

    private void streamStatic(String key, String fileName, HttpServletResponse resp) {
        try {
            resp.setContentType(detectContentType(fileName));
            String encoded = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replace("+", "%20");
            resp.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + encoded);
            OssClient oss = OssFactory.instance();
            oss.download(key, resp.getOutputStream(), resp::setContentLengthLong);
        } catch (Exception e) {
            resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        }
    }

    private static String detectContentType(String name) {
        String n = name == null ? "" : name.toLowerCase();
        if (n.endsWith(".pdf")) return "application/pdf";
        if (n.endsWith(".htm") || n.endsWith(".html")) return "text/html; charset=UTF-8";
        if (n.endsWith(".css")) return "text/css; charset=UTF-8";
        if (n.endsWith(".js")) return "application/javascript; charset=UTF-8";
        if (n.endsWith(".svg")) return "image/svg+xml";
        if (n.endsWith(".png")) return "image/png";
        if (n.endsWith(".jpg") || n.endsWith(".jpeg")) return "image/jpeg";
        if (n.endsWith(".gif")) return "image/gif";
        return "application/octet-stream";
    }
}
